#ifndef __M_FMT_H__
#define __M_FMT_H__
/* 格式化子项类的设计与实现
 *  1.满足从msg中提取不同的内容并格式化，基类实现为抽象类，包含一个统一的格式化接口
 *  2.派生类继承基类，并重写基类接口，不同派生类的重写实现不同
 *  3.通过基类指针数组便可以统一调用1种接口实现msg的格式化（否则每个类都需要定义一个指针再调用同一接口）
 *
 *  时间、等级、文件名称、行号、线程id、主体消息、日志器名称、制表符、换行符、其他信息（如'['、']'、':'等）
 */
#include <ctime>
#include <vector>
#include <string>
#include <sstream>
#include "message.hpp"
#include <cassert>

namespace log
{
    /*抽象基类*/
    class FormatItem
    {
    public:
        using ptr = std::shared_ptr<FormatItem>;
        virtual void format(std::ostream &out, const LogMsg &msg) = 0;
    };
    /*时间派生类*/
    class TimeFormatItem : public FormatItem
    {
    public:
        TimeFormatItem(const std::string &format = "%H:%M:%S") : format_(format) {}
        virtual void format(std::ostream &out, const LogMsg &msg) override
        {
            struct tm t;
            localtime_r(&msg.ctime_, &t);
            char tmp[128];
            strftime(tmp, 127, format_.c_str(), &t);
            tmp[127] = '\0';
            out << tmp;
        }

    private:
        std::string format_;
    };
    /*等级派生类*/
    class LevelFormatItem : public FormatItem
    {
    public:
        virtual void format(std::ostream &out, const LogMsg &msg) override
        {
            out << log::LogLevel::to_string(msg.level_);
        }
    };
    /*文件名派生类*/
    class FileFormatItem : public FormatItem
    {
    public:
        virtual void format(std::ostream &out, const LogMsg &msg) override
        {
            out << msg.file_;
        }
    };
    /*行号派生类*/
    class LineFormatItem : public FormatItem
    {
    public:
        virtual void format(std::ostream &out, const LogMsg &msg) override
        {
            out << msg.line_;
        }
    };
    /*线程id派生类*/
    class ThreadFormatItem : public FormatItem
    {
    public:
        virtual void format(std::ostream &out, const LogMsg &msg) override
        {
            out << msg.tid_;
        }
    };
    /*主体消息派生类*/
    class MsgFormatItem : public FormatItem
    {
    public:
        virtual void format(std::ostream &out, const LogMsg &msg) override
        {
            out << msg.payload_;
        }
    };
    /*日志器名派生类*/
    class NameFormatItem : public FormatItem
    {
    public:
        virtual void format(std::ostream &out, const LogMsg &msg) override
        {
            out << msg.name_;
        }
    };
    /*制表符派生类*/
    class TabFormatItem : public FormatItem
    {
    public:
        virtual void format(std::ostream &out, const LogMsg &msg) override
        {
            out << "\t";
        }
    };
    /*换行符派生类*/
    class NewLineFormatItem : public FormatItem
    {
    public:
        virtual void format(std::ostream &out, const LogMsg &msg) override
        {
            out << "\n";
        }
    };
    /*其他信息派生类，直接输出原生信息*/
    class OtherFormatItem : public FormatItem
    {
    public:
        OtherFormatItem(const std::string &str = "") : str_(str) {}
        virtual void format(std::ostream &out, const LogMsg &msg) override
        {
            out << str_;
        }

    private:
        std::string str_;
    };

    /* Formatter 格式化器
        %d ⽇期
        %T 缩进
        %t 线程id
        %p ⽇志级别
        %c ⽇志器名称
        %f ⽂件名
        %l ⾏号
        %m ⽇志消息
        %n 换⾏

        1.实现对格式化字符串的解析
        2.解析出来的同时配合LogMsg进行格式化输出，因为LogMsg保存的是具体数据，而格式化确定的是数据输出的格式
        3.至于是输出到屏幕，还是输出到文件，还是输出到其他地方，不归Formatter管，Formatter只需要把LogMsg按照
        要求的pattern模式格式化为一个字符串strstream即可
    */
    class Formatter
    {
    public:
        using ptr = std::shared_ptr<Formatter>;
        Formatter(const std::string &pattern = "[%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n")
            :pattern_(pattern) {
                assert(parse_pattern());
        }
        // msg格式化
        void format(std::ostream &out, const LogMsg &msg) {
            for (auto &item : items_) {
                item->format(out, msg);
            }
        }
        std::string format(const LogMsg &msg) {
            std::stringstream ss;
            format(ss, msg);
            return ss.str();
        }
        private:
        // 对格式化字符串进行解析
        // "[%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n"
        bool parse_pattern() {
            // 1.对格式化字符串进行解析，结果以pair<key, val>的形式按顺序保存在数组中
            std::vector<std::pair<std::string, std::string>> fmt_order;
            size_t pos = 0;
            size_t len = pattern_.size();
            std::string key, val;
            while (pos < len) {
                // 当前字符是否是'%'，不是说明是普通字符，直接插入val中
                if (pattern_[pos] != '%') { val.push_back(pattern_[pos++]); continue; }
                // 到这里说明pos是'%'字符，如果pos下一个位置也是'%'字符则表示是普通%字符
                if (pos + 1 < len && pattern_[pos + 1] == '%') { 
                    val.push_back('%');
                    pos += 2; 
                    continue; 
                }
                // 到这里说明pos下一个位置不是'%'，而是格式化字符
                // 先处理普通字符串，记得清理val
                if (!val.empty()) {
                    fmt_order.push_back(std::make_pair("", val));
                    val.clear();
                }

                // 获取格式化字符
                pos += 1;
                // 这里pos可能 == len，即%后没有字符了，需要判断一下
                if (pos == len) {
                    std::cout << "bool parse_pattern() %之后没有对应的格式化字符\n";
                    return false;
                }
                key = pattern_[pos];
                pos += 1;
                // 这里如果是d，会包含日期的具体格式，以花括号{}包括在内，需要特别处理
                if (pos < len && pattern_[pos] == '{') {
                    pos += 1;
                    while (pos < len && pattern_[pos] != '}') {
                        val.push_back(pattern_[pos++]);
                    }
                    // 如果是pos走到最后也没找到右}，则pattern_不合法，返回错误
                    if (pos == len) {
                        std::cout << "bool parse_pattern() 子规则{}匹配出错!\n";
                        return false;
                    }
                    // 这里pos位置就是右}位置，需要再往后走一步
                    pos += 1;
                }
                
                // 这里就是 处理格式化字符
                fmt_order.push_back(std::make_pair(key, val));
                key.clear();
                val.clear();
            }
            // 处理最后是普通字符的情况
            if (!val.empty()) fmt_order.push_back(std::make_pair(key, val));
            // 2.遍历数组元素，创建子项类的智能指针对象并尾插到items_中进行初始化
            for (auto &it : fmt_order) {
                items_.push_back(create_item(it.first, it.second));
            }
            return true;
        }
        // 根据不同的格式化字符创建不同的格式化子项对象
        FormatItem::ptr create_item(const std::string &key, const std::string &val) {
            /* Formatter 格式化器
                %d ⽇期
                %T 缩进
                %t 线程id
                %p ⽇志级别
                %c ⽇志器名称
                %f ⽂件名
                %l ⾏号
                %m ⽇志消息
                %n 换⾏
            */
           if (key == "d") return std::make_shared<TimeFormatItem>(val);
           if (key == "T") return std::make_shared<TabFormatItem>();
           if (key == "t") return std::make_shared<ThreadFormatItem>();
           if (key == "p") return std::make_shared<LevelFormatItem>();
           if (key == "c") return std::make_shared<NameFormatItem>();
           if (key == "f") return std::make_shared<FileFormatItem>();
           if (key == "l") return std::make_shared<LineFormatItem>();
           if (key == "m") return std::make_shared<MsgFormatItem>();
           if (key == "n") return std::make_shared<NewLineFormatItem>();
           // other
           if (key == "") return std::make_shared<OtherFormatItem>(val);
           std::cout << "没有这个格式化字符: " << key << std::endl;
           abort();
        }
    private:
        std::string pattern_;// 格式化字符串的具体格式，可以外部指定，也可以使用默认格式
        std::vector<FormatItem::ptr> items_;// 对格式化字符串解析后，生成的派生类智能指针对象数组
    };
}

#endif
/*
格式化类设计的思想
问：你是怎么设计格式化类的？为什么这么设计？设计思路是什么？

*/